/*
   Copyright (c) 2023 李伟国
   learner-platform is licensed under Mulan PSL v2.
   You can use this software according to the terms and conditions of the Mulan PSL v2.
   You may obtain a copy of Mulan PSL v2 at:
               http://license.coscl.org.cn/MulanPSL2
   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
   See the Mulan PSL v2 for more details. 
*/

package cn.platform.shiro;

import cn.platform.constant.RedisConstant;
import cn.platform.entity.Permission;
import cn.platform.entity.Role;
import cn.platform.entity.User;
import cn.platform.service.IPermissionService;
import cn.platform.service.IRoleService;
import cn.platform.service.IUserService;
import cn.platform.shiro.jwt.JWTToken;
import cn.platform.utils.JWTUtil;
import cn.platform.utils.RedisUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.auth0.jwt.exceptions.TokenExpiredException;


import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

@Component
public class LearnerJWTJdbcRealm extends AuthorizingRealm {
    private static final Logger log = LoggerFactory.getLogger(LearnerJWTJdbcRealm.class);
    @Autowired
    private IUserService userService;
    @Autowired
    private IRoleService roleService;
    @Autowired
    private IPermissionService permissionService;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if(principals == null){
            throw new AuthorizationException("PrincipalCollection方法参数不能为空.");
        }
        String jwt = (String)this.getAvailablePrincipal(principals);
        String username = JWTUtil.getUsername(jwt);
        Set<String> roleNameSet = new LinkedHashSet<>();  // 角色集合
        Set<String> permissionSet = new LinkedHashSet<>();  // 授权集合
        System.out.println(username);
        List<Role> roles = roleService.findByUsername(username);
        for(Role role: roles){
            roleNameSet.add(role.getName());
            List<Permission> permissionList = permissionService.findByRoleId(role.getId());
            for(Permission permission : permissionList){
                permissionSet.add(permission.getAuthorization());
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNameSet);
        info.setStringPermissions(permissionSet);
        return info;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.info("=== 认证 ===");
        String jwt = (String) token.getCredentials();
        String username = JWTUtil.getUsername(jwt);
        if(username == null){
            throw new AuthenticationException("token中无用户");
        }
        User user = userService.findByUsername(username);
        if(user == null){
            throw new AuthenticationException("用户不存在");
        }
        String tokenRedis = RedisConstant.LOGIN_USER.getValue()+jwt;
        if(redisUtil.hasKey(tokenRedis)){
            if(!JWTUtil.verify(jwt)){
                throw new TokenExpiredException("token认证失败，token过期，重新登录",null);
            }
            else if(!redisUtil.get(tokenRedis).equals(username)){
                throw new TokenExpiredException("登录账号不一致",null);
            }
            else{
                // 刷新登录时间
                long expire = redisUtil.getExpire(tokenRedis);
                if(expire > 0L){
                    redisUtil.expire(tokenRedis, 30*60);
                }
                return new SimpleAuthenticationInfo(jwt, jwt, "LearnerJWTJdbcRealm");
            }
        }else{
            throw new AuthenticationException("token过期或者Token错误！");
        }
    }
}
